package com.wayhome.srpingbootmybatis.config;

import com.alibaba.fastjson.JSONObject;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.Protocol;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.internal.Mimetypes;
import com.amazonaws.services.s3.model.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.http.entity.ContentType;
import org.apache.poi.util.IOUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.commons.CommonsMultipartFile;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.Objects;

@Slf4j
public class CSPService {
    private AmazonS3 s3 ;
    private String endpoint;
    private String accessKey;
    private String secretKey;

    public CSPService(String endpoint, String accessKey, String secretKey){
        this.accessKey = accessKey;
        this.endpoint = endpoint;
        this.secretKey = secretKey;
    }

    @PostConstruct
    public void init(){
        if (StringUtils.isEmpty(endpoint)){
            throw new RuntimeException("CSP init fail, require endpoint");
        }
        if (StringUtils.isEmpty(accessKey)){
            throw new RuntimeException("CSP init fail, require accessKey");
        }
        if (StringUtils.isEmpty(secretKey)){
            throw new RuntimeException("CSP init fail, require secretKey");
        }
        AWSCredentials credentials = new BasicAWSCredentials(accessKey, secretKey);
        ClientConfiguration clientConfig = new ClientConfiguration();
        clientConfig.setSignerOverride("S3SignerType");
        clientConfig.setProtocol(Protocol.HTTP);
        s3 = new AmazonS3Client(credentials, clientConfig);
        s3.setEndpoint(endpoint);
        log.info("CSP init success，endpoint is {}",endpoint);
    }

    /**
     * 上传文件
     * @param file 文件对象
     * @param bucket 桶名称
     * @param fileName 文件名称，用于唯一标识文件
     * @return 返回 path + fileName
     * @throws Exception
     */
    public String uploadFile(MultipartFile file, String bucket, String fileName) throws Exception{
        return uploadFile(file,bucket,fileName,"");
    }
    /**
     * 上传文件
     * @param file 文件对象
     * @param bucket 桶名称
     * @param fileName 文件名称，用于唯一标识文件
     * @param path 路径名称，CSP桶之中的目录 非必填
     * @return 返回 path + fileName
     * @throws Exception
     */
    public String uploadFile(MultipartFile file, String bucket, String fileName, String path) throws Exception{
        if (file == null){
            throw new RuntimeException("CSP upload fail, file can't be null");
        }
        if (StringUtils.isEmpty(bucket)){
            throw new RuntimeException("CSP upload fail, bucket can't be null");
        }
        if (StringUtils.isEmpty(fileName)){
            throw new RuntimeException("CSP upload fail, fileName can't be null");
        }
        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentLength(file.getSize());
        if (null!=file.getContentType() ) {
            String mimeType = Mimetypes.getInstance().getMimetype("filename." + file.getContentType());
            objectMetadata.setContentType(mimeType);
        }
        AccessControlList acl = new AccessControlList();
        // 设置为可读
        acl.grantPermission(GroupGrantee.AllUsers, Permission.Read);
        String key = StringUtils.isEmpty(path) ? fileName : path + "/" + fileName;
        PutObjectResult putObjectResult = s3.putObject(new PutObjectRequest(bucket,key,file.getInputStream(),objectMetadata).withAccessControlList(acl));
        log.info("putObjectResult : {}", JSONObject.toJSONString(putObjectResult));
        if(!endpoint.endsWith("/")){
            return endpoint + "/" + bucket + "/" + key;
        }
        return endpoint+ bucket + "/" + key;
    }

    /**
     * 上传文件
     * @param file 文件对象
     * @param bucket 桶名称
     * @param fileName 文件名称，用于唯一标识文件
     * @param path 路径名称，CSP桶之中的目录 非必填
     * @return 返回 path + fileName
     * @throws Exception
     */
    public String uploadFile(MultipartFile file, String bucket, String fileName, String path,String contentType) throws Exception{
        if (file == null){
            throw new RuntimeException("CSP upload fail, file can't be null");
        }
        if (StringUtils.isEmpty(bucket)){
            throw new RuntimeException("CSP upload fail, bucket can't be null");
        }
        if (StringUtils.isEmpty(fileName)){
            throw new RuntimeException("CSP upload fail, fileName can't be null");
        }
        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentLength(file.getSize());
        if (null!=contentType ) {
            String mimeType = Mimetypes.getInstance().getMimetype("filename." + contentType);
            objectMetadata.setContentType(mimeType);
        }
        AccessControlList acl = new AccessControlList();
        // 设置为可读
        acl.grantPermission(GroupGrantee.AllUsers, Permission.Read);
        String key = StringUtils.isEmpty(path) ? fileName : path + "/" + fileName;
        PutObjectResult putObjectResult = s3.putObject(new PutObjectRequest(bucket,key,file.getInputStream(),objectMetadata).withAccessControlList(acl));
        log.info("putObjectResult : {}", JSONObject.toJSONString(putObjectResult));
        return key;
    }

    /**
     * 上传文件
     * @param file 文件对象
     * @param bucket 桶名称
     * @param fileName 文件名称，用于唯一标识文件
     * @param path 路径名称，CSP桶之中的目录 非必填
     * @return 返回 path + fileName
     * @throws Exception
     */
    public String doUpload(MultipartFile file, String bucket, String fileName, String path) throws Exception{
        if (file == null){
            throw new RuntimeException("CSP upload fail, file can't be null");
        }
        if (StringUtils.isEmpty(bucket)){
            throw new RuntimeException("CSP upload fail, bucket can't be null");
        }
        if (StringUtils.isEmpty(fileName)){
            throw new RuntimeException("CSP upload fail, fileName can't be null");
        }
        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentLength(file.getSize());
        objectMetadata.setContentType(null);
        AccessControlList acl = new AccessControlList();
        // 设置为可读
        acl.grantPermission(GroupGrantee.AllUsers, Permission.Read);
        String key = StringUtils.isEmpty(path) ? fileName : path + "/" + fileName;
        PutObjectResult putObjectResult = s3.putObject(new PutObjectRequest(bucket,key,file.getInputStream(),objectMetadata).withAccessControlList(acl));
        log.info("putObjectResult : {}", JSONObject.toJSONString(putObjectResult));
        return endpoint+ bucket + "/" + key;
    }

    /**
     * @param file
     * @param bucket
     * @param fileName
     * @param path
     * @return  返回文件下载地址
     * @throws Exception
     */
    public String uploadFile(File file , String bucket, String fileName, String path) throws Exception{
        InputStream fileInputStream = new FileInputStream(file);
        MultipartFile multipartFile = this.inputStreamToMultipartFile(fileInputStream, fileName);
        return uploadFile(multipartFile, bucket, fileName, path);
    }


    public String getFileName(String url,String filename,String path){
        return url+ "/"+path + "/" + filename;
    }

    /**
     * 下载文件
     * @param response 响应对象
     * @param bucket  桶名称
     * @param fileName 文件名称，用于唯一标识文件
     * @param newFileName 新文件名
     */
    public void download(HttpServletResponse response, String bucket, String fileName, String newFileName) throws UnsupportedEncodingException {
        download(response,bucket,fileName,"",newFileName);
    }

    /**
     * 下载文件
     * @param response 响应对象
     * @param bucket  桶名称
     * @param fileName 文件名称，用于唯一标识文件
     * @param path 路径名称，CSP桶之中的目录 非必填
     * @param newFileName 新文件名
     */
    public void download(HttpServletResponse response, String bucket, String fileName,String path, String newFileName) throws UnsupportedEncodingException {
        String key = StringUtils.isEmpty(path) ? fileName : path + "/" + fileName;
        S3Object object = s3.getObject(bucket,key);
        if (object == null){
            throw new RuntimeException("CSP download fail, can't find file " + key);
        }
        response.setCharacterEncoding("UTF-8");
        response.setContentType(object.getObjectMetadata().getContentType());
        response.addHeader("Content-Disposition", "attachment; filename=\"" + URLEncoder.encode(newFileName, "UTF-8") + "\"");
        BufferedInputStream input = new BufferedInputStream(object.getObjectContent());
        try (OutputStream myout = response.getOutputStream()){
            //开始循环下载
            byte[] buff = new byte[2048];
            int bytesRead;
            while (-1 != (bytesRead = input.read(buff, 0, buff.length))) {
                myout.write(buff, 0, bytesRead);
            }
            myout.flush();
        } catch (Exception e) {
            log.error("CSP download fail, e = ",e);
            throw new RuntimeException(e);
        }
    }


    /**
     * 获取文件流
     * @param bucket
     * @param fileName
     * @param path
     * @return
     */
    public InputStream getFileStream(String bucket, String fileName,String path) {
        String key = StringUtils.isEmpty(path) ? fileName : path + "/" + fileName;
        S3Object object = s3.getObject(bucket,key);
        return object.getObjectContent();
    }

    /**
     * 上传文件
     * @param file 文件对象
     * @param bucket 桶名称
     * @param fileName 文件名称，用于唯一标识文件
     * @param path 路径名称，CSP桶之中的目录 非必填
     * @return 返回 path + fileName
     * @throws Exception
     */
    public String uploadFileZwb(MultipartFile file, String bucket, String fileName, String path) throws Exception{
        if (file == null){
            throw new RuntimeException("CSP upload fail, file can't be null");
        }
        if (StringUtils.isEmpty(bucket)){
            throw new RuntimeException("CSP upload fail, bucket can't be null");
        }
        if (StringUtils.isEmpty(fileName)){
            throw new RuntimeException("CSP upload fail, fileName can't be null");
        }
        ObjectMetadata objectMetadata = new ObjectMetadata();
        objectMetadata.setContentLength(file.getSize());
        if (file.getContentType() != null) {
            String mimeType = Mimetypes.getInstance().getMimetype("filename." + file.getContentType());
            objectMetadata.setContentType(mimeType);
        }
        AccessControlList acl = new AccessControlList();
        // 设置为可读
        acl.grantPermission(GroupGrantee.AllUsers, Permission.Read);
        String key = StringUtils.isEmpty(path) ? fileName : path + "/" + fileName;
        PutObjectResult putObjectResult = s3.putObject(new PutObjectRequest(bucket,key,file.getInputStream(),objectMetadata).withAccessControlList(acl));
        log.info("putObjectResult : {}", JSONObject.toJSONString(putObjectResult));
        return endpoint+"/"+ bucket + "/" + key;
    }

    /**
     * 输入流转MultipartFile
     * @param is 输入流
     * @param fileName 文件名
     * @return MultipartFile文件
     * @throws Exception 异常
     * @author zhouhengyu
     * @date 2021/9/24
     */
    public MultipartFile inputStreamToMultipartFile(InputStream is, String fileName) throws Exception{
        OutputStream os = null;
        FileItem item = null;
        try {
            FileItemFactory factory = new DiskFileItemFactory(16, null);
            String textFieldName = "file";
            item = factory.createItem(textFieldName, ContentType.APPLICATION_OCTET_STREAM.toString(), false, fileName);
            os = item.getOutputStream();
            int bytesRead = 0;
            byte[] buffer = new byte[8192];
            while ((bytesRead = is.read(buffer, 0, 8192)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
        } catch (Exception e) {
            log.error("输入流转文件失败！", e);
            throw new RuntimeException("输入流转文件失败！" + e.getMessage());
        } finally {
            IOUtils.closeQuietly(os);
        }
        if (Objects.isNull(item)) {
            throw new RuntimeException( "FileItem生成失败！");
        }
        return new CommonsMultipartFile(item);
    }


}
